<template>
  <div :class="`${prefixCls}-progress`">
    <div
      :style="{width:width?width+'px':'',background:color,minHeight:strokeWidth?strokeWidth+'px':''}"
      :class="{[prefixCls+'-progress-line']:true,[prefixCls+'-progress-outer']:showText&&!followText}"
      v-if="type==='line'">
      <slot></slot>
      <span class="progress-bar" :style="lineStyle">
       <span class="progress-text" v-if="showText&&followText"><b>{{percent}}</b>%</span>
      </span>
    </div>
    <div :class="[prefixCls+'-progress-text']" v-if="showText&&!followText">{{percent}}%</div>

    <div v-else-if="type==='circle'" class="radial-progress-container" :style="containerStyle">
      <div class="radial-progress-inner" :style="innerCircleStyle">
        <slot></slot>
      </div>
      <svg class="radial-progress-bar"
           :width="diameter"
           :height="diameter"
           version="1.1"
           xmlns="http://www.w3.org/2000/svg">
        <defs>
          <radialGradient :id="'radial-gradient' + _uid"
                          :fx="gradient.fx"
                          :fy="gradient.fy"
                          :cx="gradient.cx"
                          :cy="gradient.cy"
                          :r="gradient.r">
            <stop offset="30%" :stop-color="startColor"/>
            <stop offset="100%" :stop-color="stopColor"/>
          </radialGradient>
        </defs>
        <circle :r="innerCircleRadius"
                :cx="radius"
                :cy="radius"
                fill="transparent"
                :stroke="innerStrokeColor"
                :stroke-dasharray="circumference"
                stroke-dashoffset="0"
                :stroke-linecap="strokeLinecap"
                :style="strokeStyle"></circle>
        <circle :transform="'rotate(270, ' + radius + ',' + radius + ')'"
                :r="innerCircleRadius"
                :cx="radius"
                :cy="radius"
                fill="transparent"
                :stroke="'url(#radial-gradient' + _uid + ')'"
                :stroke-dasharray="circumference"
                :stroke-dashoffset="circumference"
                :stroke-linecap="strokeLinecap"
                :style="progressStyle"></circle>
      </svg>
    </div>
  </div>
</template>

<script>
import {prefixCls} from '../prefix'
import './progress.css'

export default {
  name: `${prefixCls}Progress`,
  props: {
    type: {
      type: String,
      default: 'line',
      // validator: function (value) {
      //   return ['line', 'circle'].indexOf(value) !== -1
      // }
    },
    value: {// 进度
      type: [Number,String],
      default: 0
    },
    width: {// 宽
      type: Number,
      default: 0

    },
    color: {
      type: String,
      default: '#ebeef5'
    }, // 底环颜色
    borderColor: {
      type: String,
      default: '#41a259'
    }, // 进度条颜色
    duration: {// 动画持续时间，单位毫秒
      type: Number,
      default: 600
    },
    showText: {// 显示进度数字
      type: Boolean,
      default: true
    },
    followText: {// 进度数字跟进进度
      type: Boolean,
      default: true
    },
    diameter: {
      type: Number,
      required: false,
      default: 130
    },
    totalSteps: {
      type: Number,
      required: false,
      default: 10
    },
    completedSteps: {
      type: Number,
      required: false,
      default: 0
    },
    startColor: {
      type: String,
      required: false,
      default: '#2d8cf0'
    },
    stopColor: {
      type: String,
      required: false,
      default: '#2d8cf0'
    },
    strokeWidth: {
      type: Number,
      required: false,
      default: 15
    },
    innerStrokeWidth: {
      type: Number,
      required: false,
      default: 15
    },
    strokeLinecap: {
      type: String,
      required: false,
      default: 'round'
    },
    // animateSpeed: {
    //   type: Number,
    //   required: false,
    //   default: 1000
    // },
    innerStrokeColor: {
      type: String,
      required: false,
      default: '#e5e9f2'
    },
    fps: {
      type: Number,
      required: false,
      default: 60
    },
    timingFunc: {
      type: String,
      required: false,
      default: 'linear'
    },
    isClockwise: {
      type: Boolean,
      required: false,
      default: true
    }
  },
  data () {
    return {
      prefixCls: prefixCls,
      gradient: {
        fx: 0.99,
        fy: 0.5,
        cx: 0.5,
        cy: 0.5,
        r: 0.65
      },
      gradientAnimation: null,
      currentAngle: 0,
      strokeDashoffset: 0,

      percent: 0
    }
  },
  computed: {
    radius () {
      return this.diameter / 2
    },
    circumference () {
      return Math.PI * this.innerCircleDiameter
    },
    stepSize () {
      if (this.totalSteps === 0) {
        return 0
      }
      return 100 / this.totalSteps
    },
    finishedPercentage () {
      return this.stepSize * this.completedSteps
    },
    circleSlice () {
      return 2 * Math.PI / this.totalSteps
    },
    animateSlice () {
      return this.circleSlice / this.totalPoints
    },
    innerCircleDiameter () {
      return this.diameter - (this.innerStrokeWidth * 2)
    },
    innerCircleRadius () {
      return this.innerCircleDiameter / 2
    },
    totalPoints () {
      return this.duration / this.animationIncrements
    },
    animationIncrements () {
      return 1000 / this.fps
    },
    hasGradient () {
      return this.startColor !== this.stopColor
    },
    containerStyle () {
      return {
        height: `${this.diameter}px`,
        width: `${this.diameter}px`
      }
    },
    progressStyle () {
      return {
        height: `${this.diameter}px`,
        width: `${this.diameter}px`,
        strokeWidth: `${this.strokeWidth}px`,
        strokeDashoffset: this.strokeDashoffset,
        transition: `stroke-dashoffset ${this.duration}ms ${this.timingFunc}`
      }
    },
    strokeStyle () {
      return {
        height: `${this.diameter}px`,
        width: `${this.diameter}px`,
        strokeWidth: `${this.innerStrokeWidth}px`
      }
    },
    innerCircleStyle () {
      return {
        width: `${this.innerCircleDiameter}px`
      }
    },

    lineStyle () {
      return {
        height: this.strokeWidth ? this.strokeWidth + 'px' : '',
        lineHeight: this.strokeWidth ? this.strokeWidth + 'px' : '',
        width: this.percent + '%',
        background: this.borderColor,
        transitionDuration: `${this.duration / 1000}s`
      }
    },

  },
  methods: {
    getStopPointsOfCircle (steps) {
      const points = []
      for (let i = 0; i < steps; i++) {
        const angle = this.circleSlice * i
        points.push(this.getPointOfCircle(angle))
      }
      return points
    },
    getPointOfCircle (angle) {
      const radius = 0.5
      const x = radius + (radius * Math.cos(angle))
      const y = radius + (radius * Math.sin(angle))
      return { x, y }
    },
    gotoPoint () {
      const point = this.getPointOfCircle(this.currentAngle)
      if (point.x && point.y) {
        this.gradient.fx = point.x
        this.gradient.fy = point.y
      }
    },
    direction () {
      if (this.isClockwise) {
        return 1
      }
      return -1
    },
    changeProgress ({ isAnimate = true }) {
      this.strokeDashoffset = ((100 - this.finishedPercentage) / 100) * this.circumference * this.direction()
      if (this.gradientAnimation) {
        clearInterval(this.gradientAnimation)
      }
      if (!isAnimate) {
        this.gotoNextStep()
        return
      }
      const angleOffset = (this.completedSteps - 1) * this.circleSlice
      let i = (this.currentAngle - angleOffset) / this.animateSlice
      const incrementer = Math.abs(i - this.totalPoints) / this.totalPoints
      const isMoveForward = i < this.totalPoints
      this.gradientAnimation = setInterval(() => {
        if (isMoveForward && i >= this.totalPoints ||
            !isMoveForward && i < this.totalPoints) {
          clearInterval(this.gradientAnimation)
          return
        }
        this.currentAngle = angleOffset + (this.animateSlice * i)
        this.gotoPoint()
        i += isMoveForward ? incrementer : -incrementer
      }, this.animationIncrements)
    },
    gotoNextStep () {
      this.currentAngle = this.completedSteps * this.circleSlice
      this.gotoPoint()
    },

    _animation () {
      // 这里还是用js控制，css控制不了，因为大于50%后还要处理其他
      let clearTime
      clearTime = setInterval(() => {
        if (this.percent < this.value && this.percent < 100) {
          this.percent++
        } else {
          clearInterval(clearTime)
        }
      }, this.duration / this.value)
    }
  },
  watch: {
    totalSteps () {
      this.changeProgress({ isAnimate: true })
    },
    completedSteps () {
      this.changeProgress({ isAnimate: true })
    },
    diameter () {
      this.changeProgress({ isAnimate: true })
    },
    strokeWidth () {
      this.changeProgress({ isAnimate: true })
    },
    value(val) {
      this.percent = val
    }
  },
  created () {
    this.changeProgress({ isAnimate: false })
  },
  mounted() {
    if (this.type === 'circle') {
      // this._animation()
    }else if (this.type === 'line') {
      // 这里用css3动画的transition，设置下延时
      setTimeout(() => {
        this.percent = this.value
      }, 10)
    }
  },
  directives: {
    // 每个都指定宽高等，但每个标签又不太一致，部分共公的，这里用指令，并且是bind形式只调用一次
    // 发现每个都要写style好麻烦
    style: {
      bind (el, binding) {
        let value = binding.value
        let type = binding.modifiers
        let style = el.style
        style.width = value.radius * 2 + 'px'
        style.height = value.radius * 2 + 'px'
        if (type.pro) {
          style.strokeWidth = `${value.strokeWidth}px solid ${value.color}`
        } else if (type.circle) {
          style.left = `-${value.strokeWidth}px`
          style.top = `-${value.strokeWidth}px`
        } else if (type.left) {
          style.strokeWidth = `${value.strokeWidth}px solid ${value.borderColor}`
          style.clip = `rect(0, ${value.radius}px, ${value.radius * 2}px, 0px)`
        } else if (type.right) {
          style.strokeWidth = `${value.strokeWidth}px solid ${value.borderColor}`
          style.clip = `rect(0, ${value.radius * 2}px, ${value.radius * 2}px, ${value.radius}px)`
        }
      }
    }
  }
}
</script>
